﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Boggle;
using System.Threading;
using System.Threading.Tasks;

namespace BoggleGUI
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private bool connected = false;
        private bool connecting = false;
        private SettingsWindow settingsWindow;
        private SummaryWindow summaryWindow;
        private InfoWindow infoWindow;
        private ErrorWindow errorWindow;

        private string serverName = "localhost";

        private BoggleConnector connector;

        /// <summary>
        /// Initializes a new MainWindow
        /// </summary>
        public MainWindow()
        {
            // some WPF jazz
            InitializeComponent();
            BoardControl.WordClicked += OnWordClicked;
        }

        #region Connector Event Handlers

        private void OnConnected(object sender, EventArgs e)
        {
            connected = true;
            connecting = false;

            Application.Current.Dispatcher.Invoke(new Action(delegate
                { connectButton.Content = "Cancel"; }), null);
        }

        private void OnGameStarted(object sender, ArrayEventArgs args)
        {
            Application.Current.Dispatcher.Invoke(new Action(delegate
                { BoardControl.BoggleGrid.BoardLayout = args.strings[0]; }), null);

            Application.Current.Dispatcher.Invoke(new Action(delegate
                { BoardControl.TimeLabel.Content = args.strings[1]; }), null);

            Application.Current.Dispatcher.Invoke(new Action(delegate
                { BoardControl.Name2Label.Content = args.strings[2]; }), null);

            Application.Current.Dispatcher.Invoke(new Action(delegate
                { BoardControl.BoggleGrid.ClearSelection(); }), null);

            Application.Current.Dispatcher.Invoke(new Action(delegate
                { BoardControl.WordLabel.Content = ""; }), null);

            Application.Current.Dispatcher.Invoke(new Action(delegate
                { connectButton.Content = "Leave"; }), null);
        }

        private void OnGameStopped(object sender, ArrayEventArgs args)
        {
            Application.Current.Dispatcher.Invoke(new Action(delegate
                { BoardControl.TimeLabel.Content = 0; }), null);

            Application.Current.Dispatcher.Invoke(new Action(delegate
                { connectButton.Content = "Connect"; }), null);

            connected = false;

            Application.Current.Dispatcher.Invoke(new Action(delegate
                { ShowSummary(args.strings); }), null);
            
        }

        private void ShowSummary(string[] words)
        {
            summaryWindow = new SummaryWindow(words);
            summaryWindow.Owner = Application.Current.MainWindow;
            summaryWindow.ShowDialog();
        }

        private void OnScoreChanged(object sender, ArrayEventArgs args)
        {
            Application.Current.Dispatcher.Invoke(new Action(delegate
                { BoardControl.Score1Label.Content = args.strings[0]; }), null);

            Application.Current.Dispatcher.Invoke(new Action(delegate
                { BoardControl.Score2Label.Content = args.strings[1]; }), null);
        }

        private void OnTimeChanged(object sender, StringEventArgs args)
        {
            Application.Current.Dispatcher.Invoke(new Action(delegate
                { BoardControl.TimeLabel.Content = args.text; }), null);
        }

        private void OnTerminated(object sender, EventArgs e)
        {
            connected = false;
            connector.CloseConnection();

            Application.Current.Dispatcher.Invoke(new Action(delegate
                { ShowErrorDialog("Opponent disconnected."); }), null);

            Application.Current.Dispatcher.Invoke(new Action(delegate
                { connectButton.Content = "Connect"; }), null);

            Application.Current.Dispatcher.Invoke(new Action(delegate
                { ResetScoreBoard(); }), null);
        }

        private void ShowErrorDialog(string msg)
        {
            errorWindow = new ErrorWindow(msg);
            errorWindow.Owner = Application.Current.MainWindow;
            errorWindow.ShowDialog();
        }

        private void OnConnectionFailed(object sender, EventArgs e)
        {
            connected = false;
            connecting = false;

            Application.Current.Dispatcher.Invoke(new Action(delegate
                { ShowErrorDialog("Failed to connect.\nCheck server name and try again."); }), null);  
            
            Application.Current.Dispatcher.Invoke(new Action(delegate
                { connectButton.Content = "Connect"; }), null);            
        }

        private void OnConnectionClosed(object sender, EventArgs e)
        {
            Application.Current.Dispatcher.Invoke(new Action(delegate
                { connectButton.Content = "Connect"; }), null);

            if (connected)
            {
                connected = false;

                Application.Current.Dispatcher.Invoke(new Action(delegate
                   { ResetScoreBoard(); }), null);

                Application.Current.Dispatcher.Invoke(new Action(delegate
                    { ShowErrorDialog("Disconnected from server."); }), null);
            }            
        }

        #endregion



        #region GUI Event Handlers
        private void OnWordClicked(object sender, StringEventArgs args)
        {
            if (connected)
                connector.SendMessage("WORD " + args.text + "\n");            
        }

        private void connectButton_Click(object sender, RoutedEventArgs e)
        {
            // try to connect to the server and start a new game if one is
            // not already connected.
            if (!connected & !connecting)
            {
                connecting = true;

                ResetScoreBoard();
                
                string name = BoardControl.Name1Label.Content.ToString();
                connector = new BoggleConnector(name);

                // register event handlers
                connector.GameStarted += OnGameStarted;
                connector.TimeChanged += OnTimeChanged;
                connector.GameStopped += OnGameStopped;
                connector.ScoreChanged += OnScoreChanged;
                connector.Terminated += OnTerminated;
                connector.Connected += OnConnected;
                connector.ConnectionFailed += OnConnectionFailed;
                connector.ConnectionClosed += OnConnectionClosed;

                // try to establish a connection on a new thread
                Task t1 = new Task(() => connector.ConnectToServer(serverName));
                t1.Start();

                Application.Current.Dispatcher.Invoke(new Action(delegate
                    { connectButton.Content = "Wait.."; }), null);

            }
            // if a game is in progress, terminated it
            else if (!connecting)
            {
                connected = false;
                connector.CloseConnection();

                ResetScoreBoard();

                Application.Current.Dispatcher.Invoke(new Action(delegate
                    { connectButton.Content = "Connect"; }), null);
            }
        }

        private void infoButton_Click(object sender, RoutedEventArgs e)
        {
            infoWindow = new InfoWindow();
            infoWindow.Owner = Application.Current.MainWindow;
            infoWindow.ShowDialog();
        }

        private void settingsButton_Click(object sender, RoutedEventArgs e)
        {
            if (!connected)
            {
                settingsWindow = new SettingsWindow(
                    BoardControl.Name1Label.Content.ToString(), serverName);

                settingsWindow.SaveEvent += saveButton_Click;

                settingsWindow.Owner = Application.Current.MainWindow;
                settingsWindow.ShowDialog();
            }

        }

        private void saveButton_Click(object sender, ArrayEventArgs e)
        {
            BoardControl.Name1Label.Content = e.strings[0];
            serverName = e.strings[1];
        }
        #endregion

        private void ResetScoreBoard()
        {
            BoardControl.Name2Label.Content = "Player 2";
            BoardControl.TimeLabel.Content = "--";
            BoardControl.Score1Label.Content = 0;
            BoardControl.Score2Label.Content = 0;
        }
    }
}
